<?php

namespace Aoe\Util\Provider;

use SplObjectStorage;

/**
 * # 元素关联扩展
 *
 * @template E as MirrorIfc
 * @template O
 */
abstract class Provider
{
    /**
     * @var SplObjectStorage[]
     */
    private static array $stores = [];

    /**
     * @param E $element
     * @noinspection PhpDocSignatureInspection
     */
    protected function __construct(protected MirrorIfc $element) {}

    /**
     * @return SplObjectStorage<O>
     */
    private static function _get_store(): SplObjectStorage
    {
        $key = static::class;
        if (!isset(self::$stores[$key])) self::$stores[$key] = new SplObjectStorage();

        return self::$stores[$key];
    }


    /**
     * @param MirrorIfc $e
     *
     * @return ?O
     */
    public static function get(MirrorIfc $e): mixed
    {
        $store = self::_get_store();
        
        if (!isset($store[$e])) static::_create($store, $e);
        return $store[$e];
    }

    /**
     * ## 静态构造函数
     * * 为了能够生成不同的子对象
     *
     * @param MirrorIfc $element
     * @return O
     */
    abstract static protected function create_object(MirrorIfc $element): mixed;


    /**
     * * 因为static实时绑定，（在有子类的情况下)该方法需要独立执行(直接在构造函数中执行会有问题)。
     *
     * @param SplObjectStorage $store
     * @param MirrorIfc $element
     * @return void
     */
    private static function _create(SplObjectStorage $store, MirrorIfc $element): void
    {
        $obj             = static::create_object($element);
        $store[$element] = $obj;
        $element->addDestructor(function () use ($store, $element){
            unset($store[$element]);
        });
    }
}